#!/usr/bin/perl -T

use warnings;
use strict;
use CGI qw/:standard/;
use CGI::Carp 'fatalsToBrowser';
use Digest::MD5 qw(md5 md5_hex md5_base64);
use File::Copy;

my $safe_filename_characters = "a-zA-Z0-9_.-";
my $doc_dir = "/var/www/w/doc";
my $hash = "5f4dcc3b5aa765d61d8327deb882cf99";

my $path;
my $length;

if ( $ENV{PATH_INFO} ) {

    ($path->{tm}, $path->{file}, $path->{do}) = split(/\//,$ENV{PATH_INFO});

}
else {

    $path->{name} = "$0";

}

if ( $ENV{SCRIPT_NAME} ) {

    $path->{name} = $ENV{SCRIPT_NAME};
    $length = $ENV{'CONTENT_LENGTH'};

}
else {

    ($path->{file}, $path->{do}) = ($ARGV[0], $ARGV[1]);
    $length = 1024;

}

$path->{file} = "Home" if ! $path->{file};
$path->{file} =~ tr/ /_/;
$path->{file} =~ s/[^$safe_filename_characters]//g;

if ( $path->{file} =~ /^([$safe_filename_characters]+)$/ ) {

    $path->{file} = $1;

}
else {

    die "Filename contains invalid characters";

}

sub do_head ($) {

    my $title = shift;
    print header ();

print <<END_HTML;
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   <title>$title</title>
   <style type="text/css">
     img {border: none;}
   </style>
 </head>
 <body>
END_HTML

}


sub do_tail () {

print <<END_HTML;
   <a href='$path->{name}/-/menu'>menu</a></div>
 </body>
</html>
END_HTML

}

sub do_write ($$) {

    my ($filename, $content) = (shift,shift);

    open ( SAVEDOC, ">$doc_dir/$filename" ) or die "Can't write '$doc_dir/$filename': $!";
    print SAVEDOC $content;
    close SAVEDOC;

}

sub do_read ($) {

    my $file_path = shift;

    if ( -f "$file_path" ) {

        my @content;

        open (my $read_fh, "$file_path") || die "Can't read '$file_path': $!\n";
 
        while (defined (my $line = <$read_fh>)) {

            push @content, $line;

        }

        close $read_fh;

        return \@content;

    }

}

sub do_edit ($) {

    my $filename = shift;

    my $content = do_read("$doc_dir/$filename");

print <<EOT;
<form method='post' action='$path->{name}/$filename/save' name='editform'>
<textarea name='savetext' rows='20' cols='80' wrap='virtual'>
EOT

print join "", @$content if $content;

print <<EOT;
</textarea>
<p><input type='submit' value='Save'></p>
</form>
EOT

}

sub do_body ($) {

    my $file = shift;

    my $content = do_read("$doc_dir/$file");

    print join "", @$content if $content;

}

sub edit_link ($) {

    my $file = shift;

    print qq~   <div><a href="$path->{name}/$file/edit">edit</a> | \n~;

}

my $do;
if ( ! -f "$doc_dir/$path->{file}" ) {

    $do = 'create';

}
if ( $path->{do} ) {

    $do = $path->{do};

}
else {

    $do = 'read';

}

if ( $do eq 'new' ) {

    do_head("Name new doc");

print <<EOT;
<form method='post' action='$path->{name}/new/name'>
<input type='text' name='title' />
<p><input type='submit' value='Create' /></p>
</form>
EOT

}
elsif ( $do eq 'name' ) {

    if(param()) {

        my $title = param('title');

        $title =~ s/ /_/g;

        if ( $title =~ /^([$safe_filename_characters]+)$/ ) {

            do_head("Create $1");

            print qq~   <p><a href="$path->{name}/$title/create">create</a></p>\n~;

        } else {

            die "Filename '$title' contains invalid characters";

        }

    }
    else {

        do_head("Need a name...");

        print qq~   <p><a href="$path->{name}/page/new">name</a></p>\n~;

    }

}
elsif ( $do eq 'create' ) {

    do_head("Create $path->{file}");
    do_edit($path->{file});

}
elsif ( $do eq 'edit' ) {

    if ( -f "$doc_dir/$path->{file}" ) {
    
        do_head("Edit $path->{file}");

    }
    else {

        do_head("Create $path->{file}");

    }

    do_edit($path->{file});

}
elsif ( $do eq 'src' ) {

    do_head("Source $path->{file}");
    print "<pre>\n";
    do_body($path->{file});
    print "</pre>\n";

}
elsif ( $do eq 'save' ) {

    my $in;

    if(param()) {

        $in = param('savetext');
        do_write($path->{file}, $in);

    }

    do_head("Saved $path->{file}");

    print "<p>Saved $path->{file}</p>\n";

    do_body($path->{file});

    edit_link($path->{file});

    print "<a href='$path->{name}/$path->{file}/'>view</a> |\n";

}
elsif ( $do eq 'list' ) {

    do_head("List");

    opendir(my $dir_fh, $doc_dir) || die "Can't opendir '$doc_dir': $!\n";
    my @files = grep { /^./ && -f "$doc_dir/$_" } readdir($dir_fh);
    closedir $dir_fh;

    print "<ul>\n";

    for my $file (sort { lc($a) cmp lc($b) } @files) {

        print qq~<li><a href="$path->{name}/$file">$file</a></li>\n~;

    }

    print "</ul>\n";

}
elsif ( $do eq 'menu' ) {

    do_head("Menu");

print <<EOT;

    <ul>
      <li><a href="$path->{name}/-/list">list</a></li>
      <li><a href="$path->{name}/-/new">new</a></li>
      <li><a href="$path->{name}/-/mv">move</a></li>
      <li><a href="$path->{name}/-/del">del</a></li>
    </ul>

EOT

}
elsif ( $do eq 'mv' ) {

    do_head("Doc to move");

    if ( $path->{file} eq '-' ) {

print <<EOT;
<p>Move what?:</p>
<form method='post' action='$path->{name}/$path->{file}/move'>
EOT

        opendir(my $dir_fh, $doc_dir) || die "Can't opendir '$doc_dir': $!\n";
        my @files = grep { /^./ && -f "$doc_dir/$_" } readdir($dir_fh);
        closedir $dir_fh;

        print "<ul>\n";

        for my $file (sort { lc($a) cmp lc($b) } @files) {

            print qq~<li><input type='radio' name='from' value='$file' /> $file</li>\n~;

        }

        print "</ul>\n";

    }
    else {

print <<EOT;
<p>Move '$path->{file}'?:</p>
<form method='post' action='$path->{name}/$path->{file}/move'>
<input type='hidden' name='from' value='$path->{file}' />
EOT

    }

print <<EOT;
<input type='text' name='to' />
<p><input type='submit' value='Move' /></p>
</form>
EOT

}
elsif ( $do eq 'move' ) {

    if(param()) {

        my $source = param('from');
        my $target = param('to');

        if ( $source =~ m{/} ) {

            die "Invalid source '$source'.\n";

        }
        elsif ( $target =~ m{/} ) {

            die "Invalid target '$target'.\n";

        }
        elsif ( -f "$doc_dir/$target" ) {

            die "Can't move '$source', '$target' exists\n";

        }
        elsif ( ! -f "$doc_dir/$source" ) {      

            die "Can't move '$source'. File not found.\n";

        }
        else {

            $target =~ s/ /_/g;

            my ($mv_from, $mv_to);

            if ( $source =~ /^([$safe_filename_characters]+)$/ ) {

                $mv_from = $1;

            } else {

                die "'$source' contains invalid characters";

            }

            if ( $target =~ /^([$safe_filename_characters]+)$/ ) {

                $mv_to = $1;

            } else {

                die "'$target' contains invalid characters";

            }

            move("$doc_dir/$mv_from", "$doc_dir/$mv_to");

            do_head("Moved...");

            print "<p>Moved to <a href='$path->{name}/$mv_to'>$mv_to</a> ...</p>\n";

        }

    }

}
elsif ( $do eq 'del' ) {

    do_head("Doc to delete");

    if ( $path->{file} eq '-' ) {

print <<EOT;
<p>Delete what?:</p>
<form method='post' action='$path->{name}/$path->{file}/confirm'>
<input type='text' name='title' />
EOT

    }
    else {

print <<EOT;
<p>Confirm delete '$path->{file}'?:</p>
<form method='post' action='$path->{name}/$path->{file}/confirm'>
<input type='hidden' name='title' value='$path->{file}' />
EOT

    }

print <<EOT;
<p><input type='submit' value='Delete' /></p>
</form>
EOT

}
elsif ( $do eq 'confirm' ) {

    do_head("Confirm delete");

    if(param()) {

        my $file = param('title');

        if ( -f "$doc_dir/$file" ) {
    
            do_body($file);

            print "<hr />\n";

            print "Delete '$file'?\n";

            print qq~   <div><a href="$path->{name}/$file/read">no</a>\n~;
            print qq~   <a href="$path->{name}/$file/delete">yes</a></div>\n~;

        }
        else {

            print "'$file' not found\n";

        }

    }

}
elsif ( $do eq 'delete' ) {

    do_head("Enter password for delete");

print <<EOT;
<form method='post' action='$path->{name}/$path->{file}/kill'>
<input type='text' name='passwd' />
<p><input type='submit' value='Delete' /></p>
</form>
EOT

}
elsif ( $do eq 'kill' ) {

    do_head("Deleting $path->{file}");

    if(param()) {

        my $digest = md5_hex(param('passwd'));

        if ( $digest eq $hash ) {

            unlink ("$doc_dir/$path->{file}")
                or die "Can't remove '$path->{file}': $!\n";
 
            print "<p>Deleted</p>\n";

        }

    }

}
else {

    do_head($path->{file});

    do_body($path->{file});

    print "<hr />\n";

    edit_link($path->{file});

}

do_tail();

